package com.ryan.mdp.reference;

import java.util.Iterator;
import java.util.List;

import javax.jms.ConnectionFactory;

import org.aopalliance.aop.Advice;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;

import com.ryan.mdp.advice.AdviceLoader;
import com.ryan.mdp.annotation.parser.MdpMethodParser;
import com.ryan.mdp.annotation.parser.MdpServiceConfig;
import com.ryan.mdp.json.JsonMessageConverter;

public class MdpReferenceFactoryBean implements FactoryBean, InitializingBean, BeanClassLoaderAware, DisposableBean {
    private ClassLoader classLoader;

    private Object proxy;

    private Class<?> interfaze;

    private String[] destinations;

    private boolean pubSubDomain;

    private String concurrency;

    private boolean transacted = false;

    private long timeout;

    private String source;

    private ConnectionFactory connectionFactory;

    private MdpInvokerClientInterceptor interceptor;

    public Object getObject() throws Exception {
        return proxy;
    }

    public Class<?> getObjectType() {
        return interfaze;
    }

    public boolean isSingleton() {
        return true;
    }

    public void afterPropertiesSet() throws Exception {
        initMdpInvokerClientInterceptor();
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.addInterface(getInterfaze());
        List<Advice> referenceAdvices = AdviceLoader.loadReferenceAdvices();
        for (Iterator<Advice> iterator = referenceAdvices.iterator(); iterator.hasNext();) {
            Advice advice = iterator.next();
            proxyFactory.addAdvice(advice);
        }
        proxyFactory.addAdvice(interceptor);
        proxy = proxyFactory.getProxy(classLoader);
    }

    public void setBeanClassLoader(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }

    public void destroy() throws Exception {
        AdviceLoader.unloadReferenceAdvices();
    }

    private void initMdpInvokerClientInterceptor() {
        interceptor = new MdpInvokerClientInterceptor();
        interceptor.setConnectionFactory(connectionFactory);
        interceptor.setDestinations(destinations);
        interceptor.setPubSubDomain(pubSubDomain);
        interceptor.setConcurrency(concurrency);
        interceptor.setTimeout(timeout);
        interceptor.setSource(source);
        interceptor.setTransacted(transacted);
        MdpServiceConfig mdpServiceConfig = MdpMethodParser.parseMdpInterface(interfaze);
        interceptor.setMdpServiceConfig(mdpServiceConfig);
        interceptor.setMessageConverter(new JsonMessageConverter());
    }

    public Class<?> getInterfaze() {
        return interfaze;
    }

    public void setInterfaze(Class<?> interfaze) {
        this.interfaze = interfaze;
    }

    public String[] getDestinations() {
        return destinations;
    }

    public void setDestinations(String[] destinations) {
        this.destinations = destinations;
    }

    public boolean isPubSubDomain() {
        return pubSubDomain;
    }

    public void setPubSubDomain(boolean pubSubDomain) {
        this.pubSubDomain = pubSubDomain;
    }

    public String getConcurrency() {
        return concurrency;
    }

    public void setConcurrency(String concurrency) {
        this.concurrency = concurrency;
    }

    public boolean isTransacted() {
        return transacted;
    }

    public void setTransacted(boolean transacted) {
        this.transacted = transacted;
    }

    public long getTimeout() {
        return timeout;
    }

    public void setTimeout(long timeout) {
        this.timeout = timeout;
    }

    public String getSource() {
        return source;
    }

    public void setSource(String source) {
        this.source = source;
    }

    public ConnectionFactory getConnectionFactory() {
        return connectionFactory;
    }

    public void setConnectionFactory(ConnectionFactory connectionFactory) {
        this.connectionFactory = connectionFactory;
    }
}
